package cn.wolfcode.wolf2w.note.service.impl;

import cn.wolfcode.wolf2w.common.core.constant.SecurityConstants;
import cn.wolfcode.wolf2w.common.core.domain.R;
import cn.wolfcode.wolf2w.common.core.exception.ServiceException;
import cn.wolfcode.wolf2w.common.core.utils.StringUtils;
import cn.wolfcode.wolf2w.common.redis.service.RedisService;
import cn.wolfcode.wolf2w.common.redis.util.RedisKeys;
import cn.wolfcode.wolf2w.common.security.utils.SecurityUtils;
import cn.wolfcode.wolf2w.destination.api.RemoteDestinationService;
import cn.wolfcode.wolf2w.destination.api.domain.Destination;
import cn.wolfcode.wolf2w.member.api.RemoteUserInfoService;
import cn.wolfcode.wolf2w.member.api.domain.UserInfo;
import cn.wolfcode.wolf2w.note.api.domain.Note;
import cn.wolfcode.wolf2w.note.api.domain.NoteContent;
import cn.wolfcode.wolf2w.note.mapper.NoteContentMapper;
import cn.wolfcode.wolf2w.note.mapper.NoteMapper;
import cn.wolfcode.wolf2w.note.query.NoteCondition;
import cn.wolfcode.wolf2w.note.query.NoteQuery;
import cn.wolfcode.wolf2w.note.service.INoteContentService;
import cn.wolfcode.wolf2w.note.service.INoteService;
import cn.wolfcode.wolf2w.note.util.DateUtil;
import cn.wolfcode.wolf2w.note.vo.AuditVO;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.validation.constraints.NotNull;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 旅游日记Service业务层处理
 *
 * @author dalang
 * @date 2023-11-26
 */
@Service
@Transactional
public class NoteServiceImpl extends ServiceImpl<NoteMapper, Note> implements INoteService {

    @Autowired
    private RemoteUserInfoService remoteUserInfoService;
    @Autowired
    private INoteContentService noteContentService;
    @Autowired
    private NoteContentMapper noteContentMapper;
    @Autowired
    private RedisService redisService;
    @Autowired
    private RemoteDestinationService remoteDestinationService;

    @Override
    public IPage<Note> queryPage(NoteQuery qo) {
        IPage<Note> page = new Page<>(qo.getCurrentPage(), qo.getPageSize());
        LambdaQueryChainWrapper<Note> wrapper = lambdaQuery()
                .like(!StringUtils.isEmpty(qo.getTitle()), Note::getTitle, qo.getTitle())
                .eq(qo.getIsPublic() != null, Note::getIsPublic, qo.getIsPublic())
                .eq(qo.getStatus() != null, Note::getStatus, qo.getStatus())
                .eq(qo.getDestId() != null, Note::getDestId, qo.getDestId());

        NoteCondition day = NoteCondition.MAP_DAY.get(qo.getDayType());
        NoteCondition month = NoteCondition.MAP_TIME.get(qo.getTravelTimeType());
        NoteCondition consume = NoteCondition.MAP_CONSUME.get(qo.getConsumeType());
        if (day!=null) {
            wrapper.between(Note::getDays,day.getMin(),day.getMax());
        }
        if (month!=null) {
            wrapper.apply("MONTH(travel_time) BETWEEN {0} and {1}",month.getMin(),month.getMax());
        }
        if (consume!=null) {
            wrapper.between(Note::getAvgConsume,consume.getMin(),consume.getMax());
        }

        wrapper.orderByDesc(StringUtils.hasText(qo.getOrderBy()),Note.ORDER_BY_VIEW.equals(qo.getOrderBy())?Note::getViewnum:Note::getCreateTime);

        wrapper.page(page);
        for (Note note : page.getRecords()) {
            Long authorId = note.getAuthorId();
            R<UserInfo> ret = remoteUserInfoService.getOne(authorId, SecurityConstants.INNER);

            if (ret != null && R.isSuccess(ret)) {
                note.setAuthor(ret.getData());
            }
        }
        return page;
    }

    @Override
    public NoteContent getContent(Long id) {
        return noteContentService.getById(id);
    }

    @Override
    public void audit(AuditVO vo) {
        Note note = super.getById(vo.getId());
        if (note == null || note.getStatus() != Note.STATE_WAITING) {
            throw new ServiceException("参数异常");
        }

        if (vo.getStatus() == Note.STATE_RELEASE) {
            // 1: 改状态：通过
            note.setStatus(Note.STATE_RELEASE);
            // 2：发布时间：now
            note.setReleaseTime(new Date());
            // 3：更新时间：now
            note.setUpdateTime(new Date());
            // 4：记录审核信息（审核人，审核状态，审核备注，审核时间，审核记录...） @TODO：新建个表记录审核信息
            super.updateById(note);
        }else{
            // 1: 改状态：通过
            // 2：发布时间：now
            // 3：更新时间：now
            // 4：记录审核信息（审核人，审核状态，审核备注，审核时间，审核记录...） @TODO：新建个表记录审核信息
            LambdaUpdateWrapper<Note> wrapper = new LambdaUpdateWrapper<>();
            wrapper.eq(Note::getId,vo.getId())
                    .set(Note::getStatus,Note.STATE_REJECT)
                    .set(Note::getReleaseTime,null)
                    .set(Note::getUpdateTime,new Date());
            super.update(wrapper);
        }
    }

    @Override
    public List<Note> viewnnumTop3(Long destId) {
        return lambdaQuery().eq(Note::getDestId,destId)
                .orderByDesc(Note::getViewnum)
                .last("limit 3")
                .list();
    }

    @Override
    public Map<String, Object> viewnumIncr(Long nid) {
        return this.numIncr(nid,"viewnum",1L);
    }

    @Override
    public Map<String, Object> replynumIncr(Long nid) {
        return null;
    }

    @Override
    public Map<String, Object> sharenumIncr(Long nid) {
        return null;
    }

    @Override
    public Map<String, Object> favor(Long nid) {
        Long uid = SecurityUtils.getUserId();
        String key = RedisKeys.USER_NOTE_FAVOR.join(uid.toString());
        if (!redisService.hasKey(key)) {
            HashSet<Object> set = new HashSet<>();
            set.add(-1L);
            redisService.setCacheSet(key,set);
        }

        boolean flag = false;
        if (redisService.isCacheSetContains(key,nid)) {
            this.numIncr(nid,"favornum",-1L);
            redisService.deleteCacheSetValue(key,nid);
        }else{
            this.numIncr(nid,"favornum",1L);
            redisService.addCacheSetValue(key,nid);
            flag = true;
        }
        String nkey = RedisKeys.NOTE_HASH.join(nid.toString());
        Map<String, Object> map = redisService.getCacheMap(nkey);
        map.put("result",flag);
        return map;
    }

    @Override
    public Boolean isUserFavor(Long nid, Long uid) {
        String key = RedisKeys.USER_NOTE_FAVOR.join(uid.toString());
        boolean flag = redisService.isCacheSetContains(key, nid);
        return flag;
    }

    @Override
    public Map<String, Object> thumbsup(Long nid) {
        Long uid = SecurityUtils.getUserId();
        String key = RedisKeys.NOTE_THUMB.join(nid.toString(), uid.toString());
        boolean flag = false;

        if (!redisService.hasKey(key)) {
            Date now = new Date();
            Date end = DateUtil.getEndDate(now);
            Long time = DateUtil.getDateBetween(now, end);
            redisService.setCacheObject(key,"thumbsup",time, TimeUnit.SECONDS);
            this.numIncr(nid,"thumbsupnum",1L);
            flag = true;
        }
        String nkey = RedisKeys.NOTE_HASH.join(nid.toString());
        Map<String, Object> map = redisService.getCacheMap(nkey);
        map.put("result",flag);
        return map;
    }

    @Override
    public boolean addNote(Note note) {
//        System.out.println(note);
        NoteContent content = note.getContent();
//        try {
//            note.setTitle(URLDecoder.decode(note.getTitle(), "utf-8"));
//            note.setSummary(URLDecoder.decode(note.getSummary(), "utf-8"));
//            content.setContent(URLDecoder.decode(content.getContent(), "utf-8"));
//        } catch (UnsupportedEncodingException e) {
//            throw new RuntimeException(e);
//        }
        R<Destination> ret = remoteDestinationService.getOne(note.getDestId(), SecurityConstants.INNER);
        if (ret == null || !R.isSuccess(ret)) {
//            throw new ServiceException("非法参数");
            note.setDestName("北京");
        }else{
            note.setDestName(ret.getData().getName());
        }
        note.setAuthorId(SecurityUtils.getUserId());
        note.setCreateTime(new Date());
        note.setUpdateTime(new Date());
        note.setStatus(1);

        boolean save = super.save(note);

        content.setId(note.getId());
        noteContentMapper.insert(content);
//        boolean contentSave = noteContentService.save(content);

        return save;
    }


    @Override
    public List<Note> getByUid(Long uid) {
        return this.lambdaQuery().eq(Note::getAuthorId,uid).list();
    }

    @Override
    public IPage<Note> queryBySelf(NoteQuery qo) {
        IPage<Note> page = new Page<>(qo.getCurrentPage(), qo.getPageSize());
        Long uid = SecurityUtils.getUserId();
        page = this.lambdaQuery().eq(Note::getAuthorId, uid).page(page);

        return page;
    }

    @Override
    public String initNoteHash(Long nid) {
        String key = RedisKeys.NOTE_HASH.join(nid.toString());

        if (!redisService.hasKey(key)) {
            Note note = super.getById(nid);
            HashMap<String, Object> map = new HashMap<>();
            map.put("viewnum",note.getViewnum());
            map.put("replynum",note.getReplynum());
            map.put("favornum",note.getFavornum());
            map.put("sharenum",note.getSharenum());
            map.put("thumbsupnum",note.getThumbsupnum());
            map.put("strategyId",note.getId().intValue());

            redisService.setCacheMap(key,map);
        }

        return key;
    }

    private Map<String, Object> numIncr(Long nid,String field,Long num){
        String key = this.initNoteHash(nid);

        redisService.incrementCacheMapValue(key,field,num);
        return redisService.getCacheMap(key);
    }
}
